package domain;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import center.BaseDomain;

public class Criteria extends BaseDomain{
	
	private long id;
	private String name;
	private double priority;
	
	private Criteria subOf;
	private Project project;
	private List<Criteria> subCriterias = new ArrayList<Criteria>();
	private List<AlternativePair> alternativePairs = new ArrayList<AlternativePair>();
	private List<CriteriaPair> criteriaPairs = new ArrayList<CriteriaPair>();
	
	private List<AlternativeVector> alternativeVectors = new ArrayList<AlternativeVector>();
	
	public double getPriority() {
		return priority;
	}
	public List<AlternativeVector> getAlternativeVectors() {
		return alternativeVectors;
	}
	public void setPriority(double priority) {
		this.priority = priority;
	}
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public void setSubOf(Criteria subOf) {
		this.subOf = subOf;
	}
	public Criteria getSubOf() {
		return subOf;
	}
	public List<Criteria> getSubCriterias() {
		return subCriterias;
	}
	public Project getProject() {
		return project;
	}
	public void setProject(Project project) {
		this.project = project;
	}
	public List<AlternativePair> getAlternativePairs() {
		return alternativePairs;
	}
	public List<CriteriaPair> getCriteriaPairs() {
		return criteriaPairs;
	}
	
	// Use EAGER load to get super/sub criteria and comparison
	public static List<Criteria> getCriteriasOf(long projectId) throws SQLException {
		Connection conn = getConnection();
		PreparedStatement ps = null;
		 StringBuffer sql = new StringBuffer();
			 sql.append("select id, name, sub_of, priority from criterias where project_id = ?");
		 ps = conn.prepareStatement(sql.toString());
		 int index=1;
		 	ps.setLong(index++, projectId);
		 	
		 ResultSet rs = ps.executeQuery();
		 
		 List<Criteria> criterias = new ArrayList<Criteria>();
		 while(rs.next()){
			Criteria criteria = new Criteria();
				 criteria.setName(rs.getString("name"));
				 criteria.setId(rs.getLong("id"));
				 criteria.setPriority(rs.getDouble("priority"));
				 
				 //get super criteria
				 long subOf = rs.getLong("sub_of");
				 if(subOf!=0)
					 criteria.setSubOf(Criteria.find(subOf));
				 
				 //get sub criteria
				 criteria.findSubCriterias();
				 
				 //get comparison
				 criteria.findComparison();
			criterias.add(criteria);
		 }
		 ps.close();
		 conn.close();
		 
		 return criterias;
	}
	
	public static Criteria find(long id) throws SQLException {
		Connection conn = getConnection();
		PreparedStatement ps = null;
		 StringBuffer sql = new StringBuffer();
			 sql.append("select id, name, priority from criterias where id = ? ");
		int index=1;
		 ps = conn.prepareStatement(sql.toString());
		 	ps.setLong(index++, id);
		 ResultSet rs = ps.executeQuery();
		 
		 Criteria criteria = new Criteria();
		 while(rs.next()){
			 criteria.setName(rs.getString("name"));
			 criteria.setId(rs.getLong("id"));
			 criteria.setPriority(rs.getDouble("priority"));
		 }
		 
		 ps.close();
		 conn.close();
		 
		 return criteria;
	}
	
	public void findSubCriterias() throws SQLException {
		Connection conn = getConnection();
		PreparedStatement ps = null;
		 StringBuffer sql = new StringBuffer();
			 sql.append("select id, name, priority from criterias where sub_of = ?");
		 ps = conn.prepareStatement(sql.toString());
		 int index=1;
		 	ps.setLong(index++, this.id);
		 	
		 ResultSet rs = ps.executeQuery();
		 
		 List<Criteria> criterias = new ArrayList<Criteria>();
		 while(rs.next()){
			Criteria criteria = new Criteria();
				 criteria.setName(rs.getString("name"));
				 criteria.setId(rs.getLong("id"));
				 criteria.setPriority(rs.getDouble("priority"));
				 
				 //eager load comparison
				 criteria.findComparison();
			criterias.add(criteria);
		 }
		 
		 this.subCriterias = criterias;
		 
		 ps.close();
		 conn.close();
	}
	
	public void insert() throws SQLException{
		Connection conn = getConnection();
		PreparedStatement ps = null;
		 StringBuffer sql = new StringBuffer();
			 sql.append("insert into criterias(name, sub_of, project_id, priority) ");
			 sql.append(" values(?, ?, ?, ?)");
		 int index =1;
		 ps = conn.prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS);
		 	ps.setString(index++, this.name);
		 	if(this.subOf!=null){
		 		ps.setLong(index++, this.subOf.getId());
		 	}else{
		 		ps.setLong(index++, 0);
		 	}
		 	
		 	ps.setLong(index++, this.project.getId());
		 	ps.setDouble(index++, 0);
		 	
		 ps.executeUpdate();
		 
		 ResultSet resultSet = ps.getGeneratedKeys();
			if (resultSet.next()) {
				long generatedid = resultSet.getLong(1);
				ps.close();
				conn.close();
				this.id = generatedid;
			} else {
				 System.out.println("Cannot Get Genereated ID!!!!");
			}
	}
	
	public boolean isLeafNode(){
		boolean isLeafNode = false;
			if(this.subCriterias==null || this.subCriterias.size()==0)
				isLeafNode = true;
		return isLeafNode;
	}
	
	public void findProject() throws SQLException {
		Connection conn = getConnection();
		PreparedStatement ps = null;
		 StringBuffer sql = new StringBuffer();
			 sql.append("select p.id, p.name, p.goal, p.created_at from projects p, criterias c where p.id = c.project_id and c.id = ?  ");
		int index=1;
		 ps = conn.prepareStatement(sql.toString());
		 	ps.setLong(index++, this.id);
		 ResultSet rs = ps.executeQuery();
		 
		 Project p = new Project();
		 while(rs.next()){
			p.setName(rs.getString("name"));
		 	p.setId(rs.getLong("id"));
		 	p.setGoal(rs.getString("goal"));
		 	p.setCreated_at(rs.getTimestamp("created_at"));
		 }
		 ps.close();
		 conn.close();
		 this.project = p;
	}
	
	public void findComparison() throws SQLException {
		if(this.isLeafNode()){
			this.alternativePairs = AlternativePair.findByCriteriaId(this.id);
		}else{
			this.criteriaPairs = CriteriaPair.findByCriteriaId(this.id);
		}
	}
	
	public void findAlternativeVector() throws SQLException{
		this.alternativeVectors = AlternativeVector.findByCriteriaId(this.id);
	}
	
	//need to do findSubCriterias and findComparison() before executing
	public void createVector() throws SQLException {
		if(this.isLeafNode()){
			createAlternativeVector();
		}else{
			createCriteriaVector();
		}
	}
	
	private void createAlternativeVector() throws SQLException{
		this.findProject();
		this.getProject().findAlternatives();
		List<Alternative> subCriteria =  this.getProject().getAlternatives();
		double [][] metrix = new double[subCriteria.size()+1][subCriteria.size()+1];
		
		ArrayList<Long> criteriaIds = new ArrayList<Long>();
		
		for(int i=0; i<this.alternativePairs.size(); i++){
			AlternativePair ap = alternativePairs.get(i);
			if(i==0){
				criteriaIds.add(ap.getSubject().getId());
				criteriaIds.add(ap.getObject().getId());
			}else{
				if(!criteriaIds.contains(ap.getSubject().getId())){
					criteriaIds.add(ap.getSubject().getId());
				}
				
				if(!criteriaIds.contains(ap.getObject().getId())){
					criteriaIds.add(ap.getObject().getId());
				}
			}
		}
		
		for(int i=0; i<criteriaIds.size(); i++){
			metrix[i+1][0] = criteriaIds.get(i);
			metrix[0][i+1] = criteriaIds.get(i);
			metrix[i+1][i+1] = 1.0;
		}
		
		for(int i=0; i<this.alternativePairs.size(); i++){
			AlternativePair ap = this.alternativePairs.get(i);
			for(int row=0; row<subCriteria.size(); row++){
				if(metrix[row+1][0]==ap.getSubject().getId()){
					for(int col=0; col<subCriteria.size(); col++){
						if(metrix[0][col+1]==ap.getObject().getId()){
							metrix[row+1][col+1] = ap.getScore();
							metrix[col+1][row+1] = 1.0/ap.getScore();
						}
					}
				}
			}
			
		}
		
		System.out.println("");
		//Print Result
		for(int row=0; row<subCriteria.size()+1; row++){
			for(int col=0; col<subCriteria.size()+1; col++){
				System.out.print(metrix[row][col]+"\t");
			}
			System.out.println("");
		}
		
		double [] summary = new double [subCriteria.size()+1];
		for(int row=0; row<subCriteria.size()+1; row++){
				summary[row] = 0.0;
		}
		
		for(int col=0; col<subCriteria.size(); col++){
			for(int row=0; row<subCriteria.size(); row++){
				summary[col+1] += metrix[row+1][col+1];
			}
		}
		
		for(int col=0; col<subCriteria.size(); col++){
			for(int row=0; row<subCriteria.size(); row++){
				metrix[row+1][col+1] = metrix[row+1][col+1]/summary[col+1];
			}
		}
		
		System.out.println("");
		//Print Result
		for(int row=0; row<subCriteria.size()+1; row++){
			for(int col=0; col<subCriteria.size()+1; col++){
				System.out.print(metrix[row][col]+"\t");
			}
			System.out.println("");
		}
		
		double [][] priority = new double [subCriteria.size()][2];
		for(int row=0; row<subCriteria.size(); row++){
			priority[row][1] = 0.0;
		}
		for(int row=0; row<subCriteria.size(); row++){
			priority[row][0] = metrix[row+1][0];
			for(int col=0; col<subCriteria.size(); col++){
				priority[row][1] += metrix[row+1][col+1];
			}
		}
		
		for(int row=0; row<subCriteria.size(); row++){
			priority[row][1] = priority[row][1]/subCriteria.size();
		}
		
		System.out.println("");
		//Print Result
		for(int row=0; row<subCriteria.size(); row++){
			for(int col=0; col<2; col++){
				System.out.print(priority[row][col]+"\t");
			}
			System.out.println("");
		}
		
		for(int row=0; row<subCriteria.size(); row++){
			AlternativeVector av = new AlternativeVector();
				av.setType("alternative");
				av.setPriority(priority[row][1]);
				av.setCriteria(this);
				av.setAlternative(Alternative.find((long) priority[row][0]));
				av.insert();
		}
	}
	
	private void createCriteriaVector() throws SQLException{
		List<Criteria> subCriteria =  this.subCriterias;
		double [][] metrix = new double[subCriteria.size()+1][subCriteria.size()+1];
		
		ArrayList<Long> criteriaIds = new ArrayList<Long>();
		
		for(int i=0; i<this.criteriaPairs.size(); i++){
			CriteriaPair cp = criteriaPairs.get(i);
			if(i==0){
				criteriaIds.add(cp.getSubject().getId());
				criteriaIds.add(cp.getObject().getId());
			}else{
				if(!criteriaIds.contains(cp.getSubject().getId())){
					criteriaIds.add(cp.getSubject().getId());
				}
				
				if(!criteriaIds.contains(cp.getObject().getId())){
					criteriaIds.add(cp.getObject().getId());
				}
			}
		}
		
		for(int i=0; i<criteriaIds.size(); i++){
			metrix[i+1][0] = criteriaIds.get(i);
			metrix[0][i+1] = criteriaIds.get(i);
			metrix[i+1][i+1] = 1.0;
		}
		
		for(int i=0; i<this.criteriaPairs.size(); i++){
			CriteriaPair cp = this.criteriaPairs.get(i);
			for(int row=0; row<subCriteria.size(); row++){
				if(metrix[row+1][0]==cp.getSubject().getId()){
					for(int col=0; col<subCriteria.size(); col++){
						if(metrix[0][col+1]==cp.getObject().getId()){
							metrix[row+1][col+1] = cp.getScore();
							metrix[col+1][row+1] = 1.0/cp.getScore();
						}
					}
				}
			}
			
		}
		System.out.println("");
		//Print Result
		for(int row=0; row<subCriteria.size()+1; row++){
			for(int col=0; col<subCriteria.size()+1; col++){
				System.out.print(metrix[row][col]+"\t");
			}
			System.out.println("");
		}
		
		double [] summary = new double [subCriteria.size()+1];
		for(int row=0; row<subCriteria.size()+1; row++){
				summary[row] = 0.0;
		}
		
		for(int col=0; col<subCriteria.size(); col++){
			for(int row=0; row<subCriteria.size(); row++){
				summary[col+1] += metrix[row+1][col+1];
			}
		}
		
		for(int col=0; col<subCriteria.size(); col++){
			for(int row=0; row<subCriteria.size(); row++){
				metrix[row+1][col+1] = metrix[row+1][col+1]/summary[col+1];
			}
		}
		System.out.println("");
		//Print Result
		for(int row=0; row<subCriteria.size()+1; row++){
			for(int col=0; col<subCriteria.size()+1; col++){
				System.out.print(metrix[row][col]+"\t");
			}
			System.out.println("");
		}
		
		double [][] priority = new double [subCriteria.size()][2];
		for(int row=0; row<subCriteria.size(); row++){
			priority[row][1] = 0.0;
		}
		for(int row=0; row<subCriteria.size(); row++){
			priority[row][0] = metrix[row+1][0];
			for(int col=0; col<subCriteria.size(); col++){
				priority[row][1] += metrix[row+1][col+1];
			}
		}
		
		for(int row=0; row<subCriteria.size(); row++){
			priority[row][1] = priority[row][1]/subCriteria.size();
		}
		
		System.out.println("");
		//Print Result
		for(int row=0; row<subCriteria.size(); row++){
			for(int col=0; col<2; col++){
				System.out.print(priority[row][col]+"\t");
			}
			System.out.println("");
		}
		
		for(int row=0; row<subCriteria.size(); row++){
			CriteriaVector cv = new CriteriaVector();
				cv.setType("criteria");
				cv.setPriority(priority[row][1]);
				cv.setCriteria(this);
				Criteria c = Criteria.find((long) priority[row][0]);
				c.setPriority(priority[row][1]);
				cv.setSubCriteria(c);
				
				cv.insert();
				c.update();
		}
	}
	
	public void update() throws SQLException{
		Connection conn = getConnection();
		PreparedStatement ps = null;
		 StringBuffer sql = new StringBuffer();
			 sql.append("update criterias set priority = ? where id = ? ");
		 int index =1;
		 ps = conn.prepareStatement(sql.toString());
		 	ps.setDouble(index++, this.priority);
		 	ps.setLong(index++, this.id);
		 ps.executeUpdate();
		 
		ps.close();
		conn.close();
	}
}
